#!/bin/bash

set -eu
set -o pipefail

PACKAGE_DIR=/var/vcap/packages/postgres-13

PERSISTENT_DISK_DIR=/var/vcap/store

STORE_DIR=${PERSISTENT_DISK_DIR}/postgres-13
STORE_DIR_OBSOLETE_10=${PERSISTENT_DISK_DIR}/postgres-10
STORE_DIR_OBSOLETE_9=${PERSISTENT_DISK_DIR}/postgres-9.4

USER='<%= p("postgres.user") %>'

sysctl -w "kernel.shmmax=67108864"

if [[ -d /var/vcap/store/postgres-15 ]]; then
  echo "Intentionally failing because data from a newer postgres job may already exist in /var/vcap/store/postgres-15"
  exit 1
fi

if [ -d ${STORE_DIR_OBSOLETE_9} ]; then
  # uh-oh, we have years-old BOSH Director
  if [ ! -d ${STORE_DIR_OBSOLETE_10} ] && [ ! -d ${STORE_DIR} ]; then
    # we never upgraded this BOSH Director from PostgreSQL 9.4 to 10
    echo "Please use a previous bosh release version (271.x or lower) to migrate data from postgres-9.4 to postgres-10."
    exit 1
  fi
  # delete the obsolete 9.4 directory to free up space
  echo "Deleting obsolete ${STORE_DIR_OBSOLETE_9} directory."
  rm -rf "${STORE_DIR_OBSOLETE_9}"
fi

if [ -d ${STORE_DIR_OBSOLETE_10} ]; then
  echo "Please use a previous bosh release version (< 280.1.10) to migrate data from postgres-10 to postgres-13."
  exit 1
fi

# We cannot kill the following conditional
# because initdb is very picky about looking at an empty dir
if [[ ! -d ${STORE_DIR} || ! -f ${STORE_DIR}/postgresql.conf ]]; then
  mkdir -p "${STORE_DIR}"
  chown vcap:vcap "${STORE_DIR}"

  # initdb creates data directories
  su - vcap -c "${PACKAGE_DIR}/bin/initdb -E utf8 -D ${STORE_DIR}"

  touch "${STORE_DIR}/fresh"

  if [ $? != 0 ]; then
    echo "ERROR: Unable to Initialize Postgres DB"
    exit 1
  fi

  echo "host all ${USER} 0.0.0.0/0 md5" >> "${STORE_DIR}/pg_hba.conf"

  mkdir -p "${STORE_DIR}/pg_log"
  chown vcap:vcap "${STORE_DIR}/pg_log"
fi

if [[ -f ${STORE_DIR}/fresh ]] ; then
  rm "${STORE_DIR}/fresh"
fi

# "bpm enforces its own locking around process operations to avoid race conditions"
# from docs: https://bosh.io/docs/bpm/runtime/
# so postmaster.pid is stale if it still exists
# remove it to prevent running into:
# FATAL:  lock file "postmaster.pid" already exists
if [[ -f ${STORE_DIR}/postmaster.pid ]] ; then
    rm "${STORE_DIR}/postmaster.pid"
fi

# The below code reindexes Postgres databases if the glibc version has changed since we last performed a reindex,
# or if we don't know when we reindexed and are running a version of glibc that made major changes to the system collation.
#  See: <https://wiki.postgresql.org/wiki/Locale_data_changes>
#       <https://www.crunchydata.com/blog/glibc-collations-and-data-corruption>
# for more information about why we do this, and why we consider glibc versions older than 2.28 to be unaffected.
#
# NOTE: If you edit this code, make sure to update the copies in all of the other Postgres versions. Extracting this into a common
#       file and sharing that file amongst the Postgres releases was just as bad as having multiple copies, so this is what we did.

rebuild_postgres_indexes()
{
  POSTGRES_DATABASE_USER="vcap"
  for database in $(echo "select datname from pg_database" | su - vcap -c "${PACKAGE_DIR}/bin/postgres --single -D ${STORE_DIR} postgres | egrep -o '\".*\"'")
  do
    echo "Going to reindex database '$database' in '${STORE_DIR}'"
    echo "REINDEX DATABASE $database" | su - vcap -c "${PACKAGE_DIR}/bin/postgres --single -D ${STORE_DIR} $database"
    # This doesn't work on Postgres versions earlier than 15, because REFRESH COLLATION VERSION isn't a thing until 15.
    echo "ALTER DATABASE $database REFRESH COLLATION VERSION" | su - vcap -c "${PACKAGE_DIR}/bin/postgres --single -D ${STORE_DIR} $database" || /bin/true
  done
}

PREVIOUS_GLIBC_VERSION_DIR=${PERSISTENT_DISK_DIR}/postgres-previous-glibc-tracking/
PREVIOUS_GLIBC_VERSION_FILE=${PREVIOUS_GLIBC_VERSION_DIR}/previous-glibc-version.txt
BREAKING_GLIBC_MAJOR=2
BREAKING_GLIBC_MINOR=28

previous_glibc_version=$(cat $PREVIOUS_GLIBC_VERSION_FILE 2>/dev/null || echo "")
previous_glibc_major=$(echo $previous_glibc_version | cut -d "." -f 1)
previous_glibc_minor=$(echo $previous_glibc_version | cut -d "." -f 2)

current_glibc_version="$(dpkg-query --show --showformat '${Version}\n' libc6 | egrep -o '^[0-9.]+')"
current_glibc_major=$(echo $current_glibc_version | cut -d "." -f 1)
current_glibc_minor=$(echo $current_glibc_version | cut -d "." -f 2)

if [[ -f $PREVIOUS_GLIBC_VERSION_FILE ]]
then
  if [[ $current_glibc_version != $previous_glibc_version ]]
  then
    rebuild_postgres_indexes
  fi
else
  # If we're newer or equal to 2.28, we'll rebuild indexes to handle the issue we know about and any future ones.
  if [[ $current_glibc_major -gt $BREAKING_GLIBC_MAJOR || $current_glibc_minor -ge $BREAKING_GLIBC_MINOR ]]
  then
    rebuild_postgres_indexes
  fi
fi

mkdir -p $PREVIOUS_GLIBC_VERSION_DIR 2>/dev/null
echo $current_glibc_version > $PREVIOUS_GLIBC_VERSION_FILE
